package my_gorm

import (
	"errors"
	"fmt"
	"gorm.io/driver/mysql"
	"gorm.io/driver/postgres"
	"gorm.io/driver/sqlserver"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"
	"io"
	"log"
	"strings"
	"time"
)

// BaseModel 不要用UpdateAt，会和gorm默认的发生冲突
type BaseModel struct {

	// 标记删除，需要手动更新
	DeletedState int   `json:"deletedState" gorm:"column:deleted_state"`
	DeletedTs    int64 `json:"deletedTs" gorm:"column:deleted_ts"`

	// 下面两个属性会在hook里自动更新
	CreatedTs int64 `json:"createdTs" gorm:"column:created_ts"`
	UpdatedTs int64 `json:"updatedTs" gorm:"column:updated_ts"`
}

type DbConfig struct {
	Host     string
	Database string
	Port     int
	User     string
	Pass     string
	Charset  string
	SqlType  string
}

// 根据配置参数生成数据库驱动 dsn，如果不传入配置则读取config/gorm.yml， 暂时不做读写分离，有待扩充
func getDsn(dbConf DbConfig) string {

	Host := dbConf.Host
	Database := dbConf.Database
	Port := dbConf.Port
	User := dbConf.User
	Pass := dbConf.Pass
	Charset := dbConf.Charset
	SqlType := dbConf.SqlType

	switch strings.ToLower(SqlType) {
	case "mysql":
		return fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=%s&parseTime=false&loc=Local", User, Pass, Host, Port, Database, Charset)
	case "sqlserver", "mssql":
		return fmt.Sprintf("server=%s;port=%d;database=%s;user id=%s;password=%s;encrypt=disable", Host, Port, Database, User, Pass)
	case "postgresql", "postgre", "postgres":
		return fmt.Sprintf("host=%s port=%d dbname=%s user=%s password=%s sslmode=disable TimeZone=Asia/Shanghai", Host, Port, Database, User, Pass)
	}
	return ""
}

// 获取一个数据库方言(Dialector),通俗的说就是根据不同的连接参数，获取具体的一类数据库的连接指针
func getDbDialector(dbConf DbConfig) (gorm.Dialector, error) {
	var dbDialector gorm.Dialector
	dsn := getDsn(dbConf)
	switch strings.ToLower(dbConf.SqlType) {
	case "mysql":
		dbDialector = mysql.Open(dsn)
	case "sqlserver", "mssql":
		dbDialector = sqlserver.Open(dsn)
	case "postgres", "postgresql", "postgre":
		dbDialector = postgres.Open(dsn)
	default:
		return nil, errors.New("错误的数据库类型")
	}
	return dbDialector, nil
}

// GetSqlDriver 该模块的入口函数，根据sqlType获取一个gorm.DB对象
func GetSqlDriver(dbConf DbConfig, w io.Writer) (*gorm.DB, error) {

	var dbDialector gorm.Dialector
	if val, err := getDbDialector(dbConf); err != nil {
		log.Println("数据库dialector初始化错误")
	} else {
		dbDialector = val
	}
	gormDb, err := gorm.Open(dbDialector, &gorm.Config{
		SkipDefaultTransaction: true,
		PrepareStmt:            true,
		Logger: logger.New(
			log.New(w, "\r\n", log.LstdFlags), // io writer
			logger.Config{
				SlowThreshold: time.Second, // Slow SQL threshold
				LogLevel:      logger.Info, // 日志级别，按详细程度Info > Warning > Error > Silent(静默)

				IgnoreRecordNotFoundError: true,  // 在日志打印中，忽视 RecordNotFound 这个错误
				ParameterizedQueries:      false, // 不打印具体参数内容，也就是只打印sql语句的代码，数据部分用问号代替
				Colorful:                  true,  // Disable color
			},
		),
	})
	if err != nil {
		//gorm 数据库驱动初始化失败
		return nil, err
	}

	// 查询没有数据，屏蔽 gorm 包中会爆出的错误
	gormDb.Statement.RaiseErrorOnNotFound = false

	// https://github.com/go-gorm/gorm/issues/4838
	_ = gormDb.Callback().Create().Before("gorm:before_create").Register("CreateBeforeHook", CreateBeforeHook)
	// 为了完美支持gorm的一系列回调函数
	_ = gormDb.Callback().Update().Before("gorm:before_update").Register("UpdateBeforeHook", UpdateBeforeHook)

	return gormDb, nil
}
